﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SharpSoft.WPF.Controls
{
    /// <summary>
    /// 相册控件
    /// </summary>
    public partial class Thumb3DControl : UserControl
    {
        public Thumb3DControl()
        {
            InitializeComponent();

            this.Loaded += ThumbControl_Loaded;
        }


        #region 滑动切换

        private double lastx;
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            lastx = e.GetPosition(this).X;
            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (AllowTouchSlide && e.LeftButton == MouseButtonState.Pressed)
            {
                var x = e.GetPosition(this).X;
                if (x < lastx)
                {
                    NextImage();
                }
                else if (x > lastx)
                {
                    PreviousImage();
                }
                lastx = x;
            }
            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);
        }

        #endregion

        private void ThumbControl_Loaded(object sender, RoutedEventArgs e)
        {
            this.RePaint();
        }

        private int _CurrentIndex;

        public int CurrentIndex
        {
            get
            {

                if (ImagesSource == null || ImagesSource.Length == 0)
                {
                    return 0;
                }
                _CurrentIndex = _CurrentIndex % ImagesSource.Length;
                while (_CurrentIndex < 0)
                {
                    _CurrentIndex += ImagesSource.Length;
                }

                return _CurrentIndex;
            }
            set
            {

                if (_CurrentIndex != value)
                {
                    if (ImagesSource == null)
                    {
                        return;
                    }
                    value = value % ImagesSource.Length;
                    while (value < 0)
                    {
                        value += ImagesSource.Length;
                    }
                    _CurrentIndex = value;
                    this.RePaint();
                }


            }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == ImagesSourceProperty || e.Property == WidthHeightRatioProperty || e.Property == VisibleCountProperty)
            {
                this.RePaint();
            }
        }

        public ImageSource[] ImagesSource
        {
            get { return (ImageSource[])GetValue(ImagesSourceProperty); }
            set { SetValue(ImagesSourceProperty, value); }
        }

        public static readonly DependencyProperty ImagesSourceProperty =
            DependencyProperty.Register("ImagesSource", typeof(ImageSource[]), typeof(Thumb3DControl), new PropertyMetadata(null));




        //public Stretch ImageStretch { get; set; } = Stretch.Uniform;

        /// <summary>
        /// 是否允许鼠标/触摸滑动切换
        /// </summary>
        public bool AllowTouchSlide { get; set; } = true;

        /// <summary>
        /// 是否允许循环切换
        /// </summary>
        public bool AllowCircle { get; set; } = false;
        /// <summary>
        /// 宽高比例，设宽度始终为1
        /// </summary>  
        public double ImageWidthHeightRatio
        {
            get { return (double)GetValue(WidthHeightRatioProperty); }
            set { SetValue(WidthHeightRatioProperty, value); }
        }

        public static readonly DependencyProperty WidthHeightRatioProperty =
            DependencyProperty.Register("WidthHeightRatio", typeof(double), typeof(Thumb3DControl), new PropertyMetadata(0.6d));


        /// <summary>
        /// 可见的图片数量
        /// </summary>  
        public int VisibleCount
        {
            get { return (int)GetValue(VisibleCountProperty); }
            set { SetValue(VisibleCountProperty, value); }
        }

        public static readonly DependencyProperty VisibleCountProperty =
            DependencyProperty.Register("VisibleCount", typeof(int), typeof(Thumb3DControl), new PropertyMetadata(5));


        /// <summary>
        /// 可视部分在圆周上所占比例,如：0.25表示可视部分为四分之一圆周。该参数只影响默认的X偏移和Z偏移的实现。
        /// 如果自定义了偏移的实现则该参数不会有任何作用。
        /// </summary>
        public double CircleRatio { get; set; } = 0.35;

        /// <summary>
        /// 获取或设置切换动画的时长，单位毫秒
        /// </summary>
        public int AnimationTimeSpan { get; set; } = 450;

        private double widthone = 2d;//单个图片宽度计作2单位长度

        private int leftCount, rightCount;//记录左右即将出现的下个索引
        /// <summary>
        /// 处理图片的旋转,指定一个函数来处理每个图块的旋转。输入参数为该图块距离中心的偏移数量。
        /// </summary>
        public OffsetParaFunc ProcessRotation;
        /// <summary>
        /// 处理图片的X偏移,指定一个函数来处理每个图块的X偏移。输入参数为该图块距离中心的偏移数量。
        /// </summary>
        public OffsetParaFunc ProcessXOffset;
        /// <summary>
        /// 处理图片的Y偏移,指定一个函数来处理每个图块的Y偏移。输入参数为该图块距离中心的偏移数量。
        /// </summary>
        public OffsetParaFunc ProcessYOffset;
        /// <summary>
        /// 处理图片的Z偏移,指定一个函数来处理每个图块的Z偏移。输入参数为该图块距离中心的偏移数量。
        /// </summary>
        public OffsetParaFunc ProcessZOffset;

        /// <summary>
        /// 用于计算图片偏移量函数
        /// </summary>
        /// <param name="index">图片距离中心位置的便宜索引</param>
        /// <param name="circleRadius">呈现的圆弧半径</param>
        /// <returns></returns>
        public delegate double OffsetParaFunc(int index, double circleRadius);

        /// <summary>
        /// 相机距离
        /// </summary>
        public double CameraDistance { get; set; } = 0;
        /// <summary>
        /// 相机视角
        /// </summary>
        public double CameraFieldOfView { get; set; } = 60;
        /// <summary>
        /// 获取指定位置的图形变换
        /// </summary>
        /// <param name="index"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="z"></param>
        /// <param name="angle"></param>
        private void getTranslate(int index, out double x, out double y, out double z, out double angle)
        {
            if (ProcessRotation != null)
            {
                angle = ProcessRotation(index,this.radius);
            }
            else
            {
                angle = ((angleone * index) / Math.PI * 180);
            }
            if (ProcessXOffset != null)
            {
                x = ProcessXOffset(index, this.radius);
            }
            else
            {
                x = Math.Sin(angleone * index) * radius; //计算当前的X偏移
            }
            if (ProcessYOffset != null)
            {
                y = ProcessYOffset(index, this.radius);
            }
            else
            {
                y = 0;
            }
            if (ProcessZOffset != null)
            {
                z = ProcessZOffset(index, this.radius);
            }
            else
            {
                z = Math.Cos(angleone * Math.Abs(index)) * radius;//计算Z偏移 
            }
        }

        int actcount;
        double angleone;
        double radius;
        private void RePaint()
        {
            vp3.Children.Clear();

            if (ImagesSource == null || ImagesSource.Length == 0)
            {
                return;
            }
            actcount = (VisibleCount / 2) * 2 + 3;//实际显示的图片数量，始终为奇数。
            angleone = Math.PI * 2 / ((1 / CircleRatio) * actcount);//一个图片所占的夹角，以弧度计。（以四分之一圆的圆弧呈现所有可见的图形）

            radius = (widthone * actcount) * (1 / CircleRatio) / (2 * Math.PI);//计算视觉圆弧的圆的半径,可视部分占圆的四分之一

            double x = 0, y = 0, z = 0;
            double angle = 0;
            rightCount = actcount / 2 + 1;
            leftCount = -rightCount;
            for (int i = actcount / 2; i >= 1; i--)
            {
                getTranslate(-i, out x, out y, out z, out angle);

                //插入左侧图像
                addView(getImage(-i), createTransform3DGroup(angle, 1, x, y, z));
            }

            getTranslate(0, out x, out y, out z, out angle);
            //插入中间图像
            addView(getImage(0), createTransform3DGroup(angle, 1, x, y, z));

            for (int i = 1; i <= actcount / 2; i++)
            {

                getTranslate(i, out x, out y, out z, out angle);
                //插入右侧图像
                addView(getImage(i), createTransform3DGroup(angle, 1, x, y, z));
            }
            var _camera = new PerspectiveCamera();

            var clang = this.CameraDistance == 0 ? radius * 2 : this.CameraDistance;//相机距画面的距离

            _camera.Position = new Point3D(0, 0, clang);
            //_camera.LookDirection = new Vector3D(0, -0.1, -5);
            //_camera.UpDirection = new Vector3D(0, 1, 0);
            _camera.FieldOfView = this.CameraFieldOfView;//相机的视角
            vp3.Camera = _camera;
        }

        /// <summary>
        /// 获取当前位置指定偏移处的图像
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        ImageSource getImage(int offset)
        {
            int index = CurrentIndex + offset;

            if (!AllowCircle && (index < 0 || index >= ImagesSource.Length))
            {
                return null;
            }
            index = index % ImagesSource.Length;
            while (index < 0)
            {
                index += ImagesSource.Length;
            }
            return ImagesSource[index];
        }

        Transform3DGroup createTransform3DGroup(double angle, double scale, double ox, double oy, double oz)
        {
            Transform3DGroup tg = new Transform3DGroup();
            tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), angle)));
            tg.Children.Add(new ScaleTransform3D(scale, scale, scale));
            tg.Children.Add(new TranslateTransform3D(ox, oy, oz));
            return tg;
        }

        void addView(ImageSource img, Transform3DGroup trans)
        {
            ModelVisual3D _visual = new ModelVisual3D();

            DiffuseMaterial dm = new DiffuseMaterial();
            GeometryModel3D geometrymodel3d = new GeometryModel3D();
            Model3DGroup model3dgroupmodel = new Model3DGroup();
            model3dgroupmodel.Children.Add(geometrymodel3d);
            dm.Brush = new ImageBrush((ImageSource)(img));
            geometrymodel3d.Material = dm;
            model3dgroupmodel.Children.Add(geometrymodel3d);

            #region light光源
            var MeshGeometry3D1 = new MeshGeometry3D();//创建模型
            var x = widthone / 2d;
            var height = widthone * ImageWidthHeightRatio;
            var y = height / 2d;
            var points = new Point3DCollection() {
                new Point3D(-x,y,0),
                new Point3D(-x,-y,0),
                new Point3D(x,-y,0),
                new Point3D(x,y,0)
            };

            MeshGeometry3D1.Positions = points;// Point3DCollection.Parse("-1,1,1 -1,-1,1 1,-1,1 1,1,1");
                                               // MeshGeometry3D1.Positions = ((Point3DCollection)new Point3DCollectionConverter().ConvertFromString("-1,1,1 -1,-1,1 1,-1,1 1,1,1"));
            MeshGeometry3D1.TriangleIndices = ((Int32Collection)new Int32CollectionConverter().ConvertFromString("0,1,3 1,2,3"));
            MeshGeometry3D1.TextureCoordinates = ((PointCollection)new PointCollectionConverter().ConvertFromString("0,0 0,1 1,1 1,0"));
            geometrymodel3d.Geometry = MeshGeometry3D1;

            AmbientLight AmbientLight1 = new AmbientLight();
            AmbientLight1.Color = Colors.Gray;
            model3dgroupmodel.Children.Add(AmbientLight1);

            DirectionalLight DirectionalLight1 = new DirectionalLight();
            DirectionalLight1.Color = Colors.Gray;
            DirectionalLight1.Direction = ((Vector3D)new Vector3DConverter().ConvertFromString("-1,-3,-2"));
            model3dgroupmodel.Children.Add(DirectionalLight1);

            DirectionalLight1 = new DirectionalLight();
            DirectionalLight1.Color = Colors.Gray;
            DirectionalLight1.Direction = ((Vector3D)new Vector3DConverter().ConvertFromString("1,-2,3"));
            model3dgroupmodel.Children.Add(DirectionalLight1);
            #endregion

            _visual.Content = model3dgroupmodel;
            _visual.Transform = trans;
            vp3.Children.Add(_visual);


        }

        private bool inMovie = false;
        /// <summary>
        /// 在切换动画完成后触发
        /// </summary>
        public event EventHandler AnimationCompleted;

        /// <summary>
        /// 上一个
        /// </summary>
        public void PreviousImage()
        {
            if (Dispatcher.Thread != System.Threading.Thread.CurrentThread)
            {
                Dispatcher.Invoke(new Action(PreviousImage));
                return;
            }

            if (ImagesSource == null || ImagesSource.Length == 0)
            {
                return;
            }
            if (!AllowCircle && CurrentIndex <= 0)
            {//不允许循环切换
                return;
            }


            if (inMovie)
            {
                return;
            }
            inMovie = true;
            Storyboard sb = new Storyboard();
            getTranslate(leftCount, out double x, out double y, out double z, out double angle);
            addView(getImage(leftCount), createTransform3DGroup(angle, 1, x, y, z));//添加左侧的图片

            var last = vp3.Children.Last();
            vp3.Children.Remove(last);
            vp3.Children.Insert(0, last);

            sb.Completed += (s, e) =>
            {
                inMovie = false;
                vp3.Children.Remove(vp3.Children.Last());
                CurrentIndex--;

                this.AnimationCompleted?.Invoke(this, null);
            };


            Transform3DGroup lasttran = null;
            foreach (var visual in vp3.Children.Reverse())
            {
                Transform3DGroup group = (Transform3DGroup)visual.Transform;

                AxisAngleRotation3D rotate = ((RotateTransform3D)(group.Children[0])).Rotation as AxisAngleRotation3D;
                TranslateTransform3D trans = ((TranslateTransform3D)(group.Children[2]));

                //动画
                DoubleAnimation dx = new DoubleAnimation(), dy = new DoubleAnimation(), dz = new DoubleAnimation();
                DoubleAnimation dr = new DoubleAnimation();
                // dx.BeginTime = dy.BeginTime = dz.BeginTime = dr.BeginTime = TimeSpan.FromMilliseconds(0);
                dx.Duration = dy.Duration = dz.Duration = dr.Duration = TimeSpan.FromMilliseconds(AnimationTimeSpan);

                dx.From = group.Value.OffsetX;
                dy.From = group.Value.OffsetY;
                dz.From = group.Value.OffsetZ;
                dr.From = ((group.Children[0] as RotateTransform3D).Rotation as AxisAngleRotation3D).Angle;
                if (lasttran != null)
                {
                    dx.To = lasttran.Value.OffsetX;
                    dy.To = lasttran.Value.OffsetY;
                    dz.To = lasttran.Value.OffsetZ;
                    dr.To = ((lasttran.Children[0] as RotateTransform3D).Rotation as AxisAngleRotation3D).Angle;
                }
                else
                {
                    dx.To = -10000;
                }

                Storyboard.SetTarget(dx, visual);
                Storyboard.SetTargetProperty(dx, new PropertyPath("Transform.Children[2].OffsetX"));
                Storyboard.SetTarget(dy, visual);
                Storyboard.SetTargetProperty(dy, new PropertyPath("Transform.Children[2].OffsetY"));
                Storyboard.SetTarget(dz, visual);
                Storyboard.SetTargetProperty(dz, new PropertyPath("Transform.Children[2].OffsetZ"));

                Storyboard.SetTarget(dr, visual);
                Storyboard.SetTargetProperty(dr, new PropertyPath("Transform.Children[0].Rotation.Angle"));

                sb.Children.Add(dx);
                sb.Children.Add(dy);
                sb.Children.Add(dz);
                sb.Children.Add(dr);
                lasttran = (Transform3DGroup)visual.Transform;

            }
            sb.Begin();
        }

        /// <summary>
        /// 下一个
        /// </summary>
        public void NextImage()
        {
            if (Dispatcher.Thread != System.Threading.Thread.CurrentThread)
            {
                Dispatcher.Invoke(new Action(NextImage));
                return;
            }
            if (ImagesSource == null || ImagesSource.Length == 0)
            {
                return;
            }
            if (!AllowCircle && CurrentIndex >= ImagesSource.Length - 1)
            {//不允许循环切换
                return;
            }
            if (inMovie)
            {
                return;
            }
            inMovie = true;
            Storyboard sb = new Storyboard();

            sb.Completed += (s, e) =>
            {
                inMovie = false;
                vp3.Children.RemoveAt(0);
                _CurrentIndex++;
                this.AnimationCompleted?.Invoke(this, null);
            };

            getTranslate(rightCount, out double x, out double y, out double z, out double angle);
            addView(getImage(rightCount), createTransform3DGroup(angle, 1, x, y, z));//添加下一张图片
            Transform3DGroup lasttran = null;
            foreach (var visual in vp3.Children)
            {
                Transform3DGroup group = (Transform3DGroup)visual.Transform;

                AxisAngleRotation3D rotate = ((RotateTransform3D)(group.Children[0])).Rotation as AxisAngleRotation3D;
                TranslateTransform3D trans = ((TranslateTransform3D)(group.Children[2]));

                //动画
                DoubleAnimation dx = new DoubleAnimation(), dy = new DoubleAnimation(), dz = new DoubleAnimation();
                DoubleAnimation dr = new DoubleAnimation();
                dx.Duration = dy.Duration = dz.Duration = dr.Duration = TimeSpan.FromMilliseconds(AnimationTimeSpan);
                //dx.From = visual.Transform.Value.OffsetX;
                if (lasttran != null)
                {
                    dx.To = lasttran.Value.OffsetX;
                    dy.To = lasttran.Value.OffsetY;
                    dz.To = lasttran.Value.OffsetZ;
                    dr.To = ((lasttran.Children[0] as RotateTransform3D).Rotation as AxisAngleRotation3D).Angle;
                }
                else
                {
                    dx.To = -10000;
                }

                Storyboard.SetTarget(dx, visual);
                Storyboard.SetTargetProperty(dx, new PropertyPath("Transform.Children[2].OffsetX"));
                Storyboard.SetTarget(dy, visual);
                Storyboard.SetTargetProperty(dy, new PropertyPath("Transform.Children[2].OffsetY"));
                Storyboard.SetTarget(dz, visual);
                Storyboard.SetTargetProperty(dz, new PropertyPath("Transform.Children[2].OffsetZ"));

                Storyboard.SetTarget(dr, visual);
                Storyboard.SetTargetProperty(dr, new PropertyPath("Transform.Children[0].Rotation.Angle"));

                sb.Children.Add(dx);
                sb.Children.Add(dy);
                sb.Children.Add(dz);
                sb.Children.Add(dr);

                lasttran = (Transform3DGroup)visual.Transform;

            }
            sb.Begin();

        }
    }
}
